import { nodes, links, getTextVisualLength, nodePopupRegistry, drawPopupNodeCurve, redrawLineOfPopupAndNode } from "./globalMgr.js";

// 创建SVG元素


export const svg = d3.select('#network');
export const width = window.innerWidth;
export const height = window.innerHeight;

svg.attr('width', width).attr('height', height)
    .call(d3.zoom()
        .scaleExtent([0.1, 10])
        .on('zoom', (event) => {
            svg.select('g.nodes-container').attr('transform', event.transform);
            svg.select('g.links-container').attr('transform', event.transform);
            // 在缩放时重绘所有弹窗连线
            redrawLineOfPopupAndNode();
        })
    );

// 确保 links-container 在 nodes-container 之下
if (svg.select('g.links-container').empty()) {
    svg.insert('g', ':first-child').attr('class', 'links-container');
}
if (svg.select('g.nodes-container').empty()) {
    svg.append('g').attr('class', 'nodes-container');
}

svg.selectAll('g.nodes-container, g.links-container')
    .attr('transform', null);

// 创建力导向图模拟
export const simulation = d3.forceSimulation(nodes)
    .force('link', d3.forceLink(links).id(d => d.id).distance(100))
    .force('charge', d3.forceManyBody().strength(-500))
    .force('center', d3.forceCenter(width / 2, height / 2))
    .force('collision', d3.forceCollide().radius(d => d.radius + 20).strength(0.7))
    .force('x', d3.forceX(width / 2).strength(0.05))
    .force('y', d3.forceY(height / 2).strength(0.05));

// 创建节点组(先创建但保持顺序)
export let node = svg.append('g')
    .attr('class', 'nodes-container')
    .selectAll('g')
    .data(nodes)
    .join('g')
    .attr('class', 'node-group')
    .call(d3.drag()
        .on('start', dragstarted)
        .on('drag', dragged)
        .on('end', dragended));

// 更新图形函数
export function updateGraph() {
    console.groupCollapsed('[Graph] Updating graph visualization');
    console.info('Current nodes:', nodes.length, 'Current links:', links.length);
    // 重新计算度数
    console.info('Calculating node degrees...');
    nodes.forEach(node => {
        const degree = links.reduce((count, link) => {
            const isConnected = (typeof link.source === 'object' ? link.source.id : link.source) === node.id ||
                (typeof link.target === 'object' ? link.target.id : link.target) === node.id;
            return count + (isConnected ? 1 : 0);
        }, 0);
        node.degree = degree;
        // radius 只由文本长度决定
    const textLen = getTextVisualLength(node.name);
    const fontSize = 14;
    const padding = 22; // 增加左右留白
    // 计算文本实际宽度
    const textWidth = textLen * fontSize * 0.6;
    // 半径至少能包住文本并留出 padding
    node.radius = Math.max(18, Math.ceil((textWidth + padding) / 2));
    });

    // 触发节点更新事件
    const nodesUpdatedEvent = new Event('nodesUpdated');
    window.dispatchEvent(nodesUpdatedEvent);
    // 更新节点，确保所有节点都在 .nodes-container <g> 下
    console.info('Updating nodes...');
    const nodesContainer = svg.select('g.nodes-container');
    const nodeGroups = nodesContainer.selectAll('.node-group')
        .data(nodes, d => d.id);
    const removedNodes = nodeGroups.exit();
    console.info('Removing nodes:', removedNodes.size());
    removedNodes.remove();
    const newNodes = nodeGroups.enter()
        .append('g')
        .attr('class', 'node-group')
        .attr('data-id', d => d.id)
        .call(d3.drag()
            .on('start', dragstarted)
            .on('drag', dragged)
            .on('end', dragended));
    console.info('Adding new nodes:', newNodes.size());
    newNodes.append('circle')
        .attr('r', d => d.radius)
        .attr('fill', d => d.color)
        .attr('stroke', d => d3.color(d.color).darker(0.5))
        .attr('stroke-width', 2);
    newNodes.append('text')
        .text(d => d.name || d.id)
        .attr('dy', '.35em')  // 垂直居中
        .attr('text-anchor', 'middle')  // 水平居中
        .attr('fill', 'white')  // 白色文字提高可读性
        .style('pointer-events', 'none')  // 避免文字干扰点击事件
        .style('font-size', d => {
            // 计算最大可用字体，保证文本不贴边
            const textLen = getTextVisualLength(d.name || d.id);
            const maxFont = Math.min(16, Math.floor((d.radius * 2 - 16) / (textLen * 0.6)));
            return Math.max(10, maxFont);
        })
        .style('font-weight', 'bold')  // 加粗提高可读性
        .attr('x', 0)
        .style('paint-order', 'stroke')
        .style('stroke', 'rgba(0,0,0,0.15)')
        .style('stroke-width', '1.5px');
    // 更新 node 全局变量，保证后续操作都用最新选择器
    node = nodesContainer.selectAll('.node-group');
    // 重新绑定节点点击弹窗事件（确保新节点可弹窗）
    if (window.bindNodeDetailPopup) window.bindNodeDetailPopup();
    // 节点hover动画（新节点也支持）
    node.on('mouseenter', function (event, d) {
        d3.select(this).select('circle')
            .transition()
            .duration(5)
            .attr('r', d.radius + 10);
    })
        .on('mouseleave', function (event, d) {
            d3.select(this).select('circle')
                .transition()
                .duration(5)
                .attr('r', d.radius);
        });
    // 更新连线 - 确保在节点下方
    console.info('Updating links...');
    const link = svg.select('.links-container')
        .selectAll('.link')
        .data(links, d => {
            const sourceId = typeof d.source === 'object' ? d.source.id : d.source;
            const targetId = typeof d.target === 'object' ? d.target.id : d.target;
            return `${sourceId}_${targetId}`;
        });
    const removedLinks = link.exit();
    console.info('Removing links:', removedLinks.size());
    removedLinks.each(function(d) {
        // 移除对应的 bulge-seg-group
        const sourceId = typeof d.source === 'object' ? d.source.id : d.source;
        const targetId = typeof d.target === 'object' ? d.target.id : d.target;
        svg.select('.links-container')
            .selectAll(`g.bulge-seg-group[data-link='${sourceId}_${targetId}']`)
            .remove();
    });
    removedLinks.remove();
    const newLinks = link.enter()
    .append('path')
    .attr('class', 'link')
    .merge(link);
    console.info('Adding new links:', newLinks.size());
    // console.debug('New links details:', links.map(link => ({
    //     source: typeof link.source === 'object' ? link.source.id : link.source,
    //     target: typeof link.target === 'object' ? link.target.id : link.target
    // })));
    console.info('Restarting simulation with new data...');
    // 彻底重启模拟
    simulation.nodes(nodes);
    simulation.force('link', d3.forceLink(links).id(d => d.id).distance(100));
    // forceLink 会自动将 links 的 source/target 变为节点对象
    // 设置初始位置避免左上角聚集
    nodes.forEach(node => {
        if (!node.x || !node.y) {
            node.x = width / 2 + (Math.random() - 0.5) * 100;
            node.y = height / 2 + (Math.random() - 0.5) * 100;
        }
    });
    // tick 里做节点、连线刷新和弹窗连线更新
    simulation.on('tick', () => {
        if (currentLinkDrawStrategy !== LinkDrawStrategies.flow) {
            currentLinkDrawStrategy.drawLink(svg.selectAll('.link'));
        }
        svg.selectAll('.node-group')
            .attr('transform', d => `translate(${d.x},${d.y})`);

        // 更新所有弹窗连线（优化版）
        redrawLineOfPopupAndNode();
    });
    simulation.alpha(1).restart();
    console.info('Graph update completed');
    console.groupEnd();
}

// 连线绘制策略模式
export const LinkDrawStrategies = {
    // 默认直线策略
    straight: {
        drawLink: function(selection) {
            selection
                .attr('d', d => `M${d.source.x},${d.source.y} L${d.target.x},${d.target.y}`)
                .attr('stroke', '#999')
                .attr('stroke-width', 2)
                .attr('stroke-opacity', 0.6)
                .attr('fill', 'none');
        }
    },
    flow: {
        // 鼓包动画全局参数
        _bulgeT: 0,
        _speedCmPerS: 2,
        _pxPerCm: 40,
        _ballRadius: 14, // 鼓包最大宽度（垂直方向）变小
        _bulgeLen: 10,   // 鼓包长度（沿管道方向）进一步收窄
        drawLink: function(selection) {
            // 关键：缓存参数，避免each里this指向错误
            const bulgeT = this._bulgeT;
            const bulgeLen = this._bulgeLen;
            const ballRadius = this._ballRadius;
            selection.each(function(d, i, nodes) {
                const sx = d.source.x, sy = d.source.y;
                const tx = d.target.x, ty = d.target.y;
                const mx = (sx + tx) / 2;
                const my = (sy + ty) / 2 + 40 + 0.15 * Math.abs(sx - tx);
                const pathStr = `M${sx},${sy} Q${mx},${my} ${tx},${ty}`;
                // 采样点
                const N = 80;
                let points = [];
                for (let j = 0; j <= N; ++j) {
                    const t = j / N;
                    const x = (1-t)*(1-t)*sx + 2*(1-t)*t*mx + t*t*tx;
                    const y = (1-t)*(1-t)*sy + 2*(1-t)*t*my + t*t*ty;
                    points.push({x, y});
                }
                // 计算总长
                let totalLen = 0;
                for (let j = 1; j < points.length; ++j) {
                    const dx = points[j].x - points[j-1].x;
                    const dy = points[j].y - points[j-1].y;
                    totalLen += Math.sqrt(dx*dx + dy*dy);
                }
                const bulgeCenter = bulgeT * totalLen;
                let accLen = 0;
                let pointsW = [Object.assign({}, points[0], {w:6})];
                for (let j = 1; j < points.length; ++j) {
                    const dx = points[j].x - points[j-1].x;
                    const dy = points[j].y - points[j-1].y;
                    const segLen = Math.sqrt(dx*dx + dy*dy);
                    accLen += segLen;
                    const dist = Math.abs(accLen - bulgeCenter);
                    const bulge = Math.exp(-0.5 * (dist / (bulgeLen/2)) ** 2);
                    const width = 6 + bulge * (ballRadius - 6);
                    pointsW.push(Object.assign({}, points[j], {w: width}));
                }
                // 只创建一次bulge-seg组
                let bulgeGroup = d3.select(this.parentNode).select(`g.bulge-seg-group[data-link='${d.source.id || d.source}_${d.target.id || d.target}']`);
                if (bulgeGroup.empty()) {
                    bulgeGroup = d3.select(this.parentNode)
                        .append('g')
                        .attr('class', 'bulge-seg-group')
                        .attr('data-link', `${d.source.id || d.source}_${d.target.id || d.target}`);
                    // 初始化N条line
                    for (let j = 1; j < pointsW.length; ++j) {
                        bulgeGroup.append('line')
                            .attr('class', 'bulge-seg')
                            .attr('stroke', 'orange')
                            .attr('stroke-linecap', 'round')
                            .attr('stroke-opacity', 0.85);
                    }
                }
                // 更新所有bulge-seg属性
                const lines = bulgeGroup.selectAll('line.bulge-seg');
                lines.data(pointsW.slice(1))
                    .attr('x1', (p, j) => pointsW[j].x)
                    .attr('y1', (p, j) => pointsW[j].y)
                    .attr('x2', (p, j) => p.x)
                    .attr('y2', (p, j) => p.y)
                    .attr('stroke-width', (p, j) => p.w);
                d3.select(this)
                    .attr('d', pathStr)
                    .attr('stroke', 'orange')
                    .attr('stroke-width', 6)
                    .attr('stroke-opacity', 1)
                    .attr('fill', 'none')
                    .raise();
            });
        }
    }
    // 可扩展更多策略，如curve、arrow等
};

// 鼓包动画独立定时器管理（使用d3.timer）
let bulgeAnimTimer = null;
let lastBulgeTime = performance.now();
function startBulgeAnimation() {
    if (bulgeAnimTimer) bulgeAnimTimer.stop();
    lastBulgeTime = performance.now();
    bulgeAnimTimer = d3.timer(function() {
        if (currentLinkDrawStrategy === LinkDrawStrategies.flow) {
            const now = performance.now();
            const dt = (now - lastBulgeTime) / 1000;
            lastBulgeTime = now;
            if (typeof currentLinkDrawStrategy._bulgeT !== 'number') currentLinkDrawStrategy._bulgeT = 0;
            // 计算所有连线的平均长度（像素）
            let totalLen = 0, count = 0;
            links.forEach(link => {
                const sx = link.source.x, sy = link.source.y;
                const tx = link.target.x, ty = link.target.y;
                const mx = (sx + tx) / 2;
                const my = (sy + ty) / 2 + 40 + 0.15 * Math.abs(sx - tx);
                // 采样
                let prev = {x: sx, y: sy};
                let len = 0;
                for (let j = 1; j <= 80; ++j) {
                    const t = j / 80;
                    const x = (1-t)*(1-t)*sx + 2*(1-t)*t*mx + t*t*tx;
                    const y = (1-t)*(1-t)*sy + 2*(1-t)*t*my + t*t*ty;
                    len += Math.sqrt((x-prev.x)**2 + (y-prev.y)**2);
                    prev = {x, y};
                }
                totalLen += len;
                count++;
            });
            const avgLenPx = count ? totalLen / count : 200;
            const avgLenCm = avgLenPx / currentLinkDrawStrategy._pxPerCm;
            // 推进bulgeT
            currentLinkDrawStrategy._bulgeT += dt * currentLinkDrawStrategy._speedCmPerS / (avgLenCm || 1);
            // 保证bulgeT始终在[0,1)区间，防止停止
            currentLinkDrawStrategy._bulgeT = currentLinkDrawStrategy._bulgeT % 1;
            if (currentLinkDrawStrategy._bulgeT < 0) currentLinkDrawStrategy._bulgeT += 1;
            // 只刷新连线，不刷新节点
            currentLinkDrawStrategy.drawLink(svg.selectAll('.link'));
        }
    });
}
function stopBulgeAnimation() {
    if (bulgeAnimTimer) {
        bulgeAnimTimer.stop();
        bulgeAnimTimer = null;
    }
}

// 当前连线绘制策略，默认流动橘黄软线
let currentLinkDrawStrategy = LinkDrawStrategies.flow;
export function setLinkDrawStrategy(strategy) {
    stopBulgeAnimation();
    if (typeof strategy === 'string' && LinkDrawStrategies[strategy]) {
        currentLinkDrawStrategy = LinkDrawStrategies[strategy];
    } else if (strategy && typeof strategy.drawLink === 'function') {
        currentLinkDrawStrategy = strategy;
    }
    if (currentLinkDrawStrategy === LinkDrawStrategies.flow) {
        startBulgeAnimation();
    }
}

// 启动默认策略动画
if (currentLinkDrawStrategy === LinkDrawStrategies.flow) {
    startBulgeAnimation();
}

// 拖拽函数
function dragstarted(event, d) {
    if (!event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
}

function dragged(event, d) {
    d.fx = event.x;
    d.fy = event.y;
}

function dragended(event, d) {
    if (!event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
}